import 'log_file_info.dart';
import 'list_options.dart';

/// 日志查询与路径计算的通用工具。
///
/// 能力概述：
/// - 统一处理不同平台路径分隔符与根路径规整；
/// - 提供按年份/月份/日期定位日志文件的便捷方法；
/// - 支持按前缀筛选与对列表应用排序/分页选项。
class LogQuery {
  /// 根据根路径判断分隔符（Windows 通常为 '\\'，其他平台为 '/'）。
  static String separator(String root) {
    return (root.contains('\\') && !root.contains('/')) ? '\\' : '/';
  }

  /// 规整根路径，去除末尾分隔符（若存在）。
  static String normalizeRoot(String root, String sep) {
    return root.endsWith(sep)
        ? root.substring(0, root.length - sep.length)
        : root;
  }

  /// 计算年份目录的绝对路径。
  ///
  /// 参数：
  /// - [root]：日志根目录绝对路径；
  /// - [year]：年份（如 `2025`）。
  /// 返回：`<root>/<year>`。
  static String yearDir(String root, int year) {
    final sep = separator(root);
    final norm = normalizeRoot(root, sep);
    final y = year.toString().padLeft(4, '0');
    return '$norm$sep$y';
  }

  /// 计算年份-月份目录的绝对路径。
  ///
  /// 参数：
  /// - [root]：日志根目录绝对路径；
  /// - [year]：年份（如 `2025`）；
  /// - [month]：月份（`1-12`）。
  /// 返回：`<root>/<year>/<month>`。
  static String yearMonthDir(String root, int year, int month) {
    final sep = separator(root);
    final norm = normalizeRoot(root, sep);
    final y = year.toString().padLeft(4, '0');
    final m = month.toString().padLeft(2, '0');
    return '$norm$sep$y$sep$m';
  }

  /// 按前缀过滤日志文件列表。
  ///
  /// 参数：
  /// - [all]：全部日志文件列表；
  /// - [prefix]：目录前缀（结尾需包含分隔符）。
  /// 返回：路径以该前缀开头的文件子集。
  static List<PDLogFile> filterByPrefix(List<PDLogFile> all, String prefix) {
    return all.where((f) => f.path.startsWith(prefix)).toList();
  }

  /// 过滤指定年份下的日志文件。
  ///
  /// 参数：
  /// - [root]：日志根目录绝对路径；
  /// - [all]：全部日志文件列表；
  /// - [year]：年份。
  /// 返回：`<root>/<year>/` 下的所有 `.log` 文件。
  static List<PDLogFile> byYear(String root, List<PDLogFile> all, int year) {
    final sep = separator(root);
    final dir = yearDir(root, year);
    final prefix = '$dir$sep';
    return filterByPrefix(all, prefix);
  }

  /// 过滤指定年份与月份下的日志文件。
  ///
  /// 参数：
  /// - [root]：日志根目录绝对路径；
  /// - [all]：全部日志文件列表；
  /// - [year]：年份；
  /// - [month]：月份。
  /// 返回：`<root>/<year>/<month>/` 下的所有 `.log` 文件。
  static List<PDLogFile> byYearMonth(
      String root, List<PDLogFile> all, int year, int month) {
    final sep = separator(root);
    final dir = yearMonthDir(root, year, month);
    final prefix = '$dir$sep';
    return filterByPrefix(all, prefix);
  }

  /// 计算指定日期对应的日志文件绝对路径（不检查存在性）。
  ///
  /// 参数：
  /// - [root]：日志根目录绝对路径；
  /// - [date]：日期对象；
  /// 返回：`<root>/<yyyy>/<MM>/<dd>.log`。
  static String dateFilePath(String root, DateTime date) {
    final sep = separator(root);
    final norm = normalizeRoot(root, sep);
    final y = date.year.toString().padLeft(4, '0');
    final m = date.month.toString().padLeft(2, '0');
    final d = date.day.toString().padLeft(2, '0');
    return '$norm$sep$y$sep$m$sep$d.log';
  }

  /// 在已知文件列表中查找指定日期的文件，存在则返回绝对路径，否则返回空字符串。
  ///
  /// 参数：
  /// - [root]：日志根目录绝对路径；
  /// - [all]：全部日志文件列表；
  /// - [date]：目标日期；
  /// 返回：存在时返回绝对路径，否则返回空字符串。
  static String findDateFilePath(
      String root, List<PDLogFile> all, DateTime date) {
    final expected = dateFilePath(root, date);
    for (final f in all) {
      if (f.path == expected) return expected;
    }
    return '';
  }

  /// 对日志文件列表应用排序与分页。
  ///
  /// - 当 [options] 为空时原样返回；
  /// - 支持按时间（`modifiedMs` 优先，其次解析 `year/month/day`）或名称排序；
  /// - 支持升序/降序；
  /// - 支持基于 `pageSize` 与 `page` 的简单分页。
  static List<PDLogFile> applyOptions(
    List<PDLogFile> files,
    ListOptions? options,
  ) {
    if (options == null) return files;
    var list = List<PDLogFile>.from(files);

    int timeMs(PDLogFile f) {
      if (f.modifiedMs > 0) return f.modifiedMs;
      final y = f.year;
      final m = f.month;
      final d = f.day;
      if (y != null && m != null && d != null) {
        try {
          return DateTime(y, m, d).millisecondsSinceEpoch;
        } catch (_) {
          return 0;
        }
      }
      return 0;
    }

    int cmp(PDLogFile a, PDLogFile b) {
      if (options.by == SortBy.time) {
        final ta = timeMs(a);
        final tb = timeMs(b);
        final c = ta.compareTo(tb);
        return c;
      } else {
        final na = a.fileName;
        final nb = b.fileName;
        return na.compareTo(nb);
      }
    }

    list.sort(cmp);
    if (options.direction == SortDirection.desc) {
      list = list.reversed.toList();
    }

    if (options.pageSize != null && (options.pageSize ?? 0) > 0) {
      final ps = options.pageSize!;
      final page = options.page <= 0 ? 1 : options.page;
      final start = (page - 1) * ps;
      if (start >= list.length) return const [];
      final end = (start + ps) > list.length ? list.length : (start + ps);
      return list.sublist(start, end);
    }
    return list;
  }
}
